import datetime
import time
import eventlet
from socket import socket
from struct import unpack
from socketio import RedisManager
from django.conf import settings
from django.core.cache import cache
from django.db import close_old_connections
from socketio import RedisManager
from apps.devices.models import *
from tcpmanage.manager import TCPManager
from . import request_type
from .config import Config
from .proto.utils import read
from .serializers import *
from .connection import Connection
from apps.user.models import User


class EventHandle:
    config = Config()
    manager = RedisManager('redis://127.0.0.1:6379/3')
    _HANDLE_FUNC_NAME = 'handle'

    def __call__(self, conn, data, handle_type='', serial_number=''):
        if handle_type:
            func = getattr(self, '%(handle_name)s_%(handle_type)s' % (
                {"handle_name": self._HANDLE_FUNC_NAME, 'handle_type': handle_type}), None)
        else:
            func = getattr(self, self._HANDLE_FUNC_NAME, None)
        func(conn, data, serial_number=serial_number)
        close_old_connections()

    def _read(self, conn: socket, timeout=settings.TASK_TIME_OUT):

        return read(conn, time_out=timeout)


class GenSidFromSerialNumber(EventHandle):
    """
        socket首次连接 获取序列号 区分设备
    """

    def __call__(self, conn: socket, connect_dict, **kwargs):

        close_old_connections()

        dev, serial_number = self.register_devices(conn)
        self.manager.emit('connect', {"devices": serial_number})
        close_old_connections()
        TCPManager.get_instance().offer(
            serial_number,
            {},
            btoi(
                request_type.CLEAN_TCP_CONNECT
            ),
            is_callback=False
        )
        cache.set(serial_number, True, timeout=120)

        close_old_connections()
        self.update_controls(conn, dev)

        close_old_connections()
        self.update_sensor(conn, dev)

        close_old_connections()
        self.update_auto_rule(conn, dev)

        close_old_connections()
        self.update_controls_group(conn, dev)

        close_old_connections()
        self.update_mock_control(conn, dev)
        close_old_connections()
        self.update_mock_sensor(conn, dev)

        close_old_connections()
        self.update_number_input(conn, dev)
        close_old_connections()
        return serial_number

    def register_devices(self, conn):

        data = self.config.get_sys_item(conn)
        device_serial_number: str = data['device_serial_number']
        data['manufacture_tmie'] = datetime.datetime.strptime(
            data['manufacture_tmie'], '%Y%m%d'
        )
        devices = Devices.objects.filter(
            device_serial_number=device_serial_number).first()
        if not devices:
            devices: Devices = Devices.objects.create(**data)
        else:
            for i in data:
                setattr(devices, i, data[i])
            devices.save()
        devices.status = True
        return devices, data['device_serial_number']

    def update_controls(self, conn, dev):

        res = self.config.get_number_output_item(conn)['settings']
        index = 1
        for i in res:
            if i['status'] != 1 or i['number'] <= 0:
                index += 1
                continue

            i.update({
                'devices': dev,
                'u_id': index,
                'number': str(index),
                'alias': str(i['number']),
            })
            controls = Controls.objects.update_or_create(
                i,
                **{
                    'u_id': index,
                    'devices_id': dev.id
                },
            )

            index += 1

    def update_sensor(self, conn, dev):

        res = self.config.get_total_line_item(conn)['settings']
        index = 1
        parse_str = [
            ['<i', '<I', '<H', '<f', '<?'],
            ['>i', '>I', '>H', '>f', '>?'],
        ]
        for i in res:
            if i['input_number'] <= 0 or i['status'] != 1:
                index += 1
                continue
          
            i.update({
                'devices_id': dev.id,
                'u_id': index
            })
            print(i)
            sensor = Sensor.objects.update_or_create(

                i,
                **{
                    'devices_id': dev.id,
                    'u_id': index
                },
            )
            index += 1

    def update_auto_rule(self, conn, dev):
        req = readConfigPackagerItem.pack(
            {'item': 11}, request_type.PAYLOAD_REQ_TO_MCU_GET_CLASS)

        conn.send(req)
        res_lis = []
        length = AutoRuleSerializer.length()

        res = req.wait()
        for i in res:
            rules = AutoRuleSerializer.unpack_subserializer(i[3:])
            res_lis += rules

        index = 1
        for i in res_lis:
            if i['rule_number'] <= 0 or i['status'] != 1:
                # 判断number和 该段内存地址是否可用
                index += 1
                continue
            i.update({
                'u_id': index,
                'devices_id': dev.id
            })

            obj = AutoRule.objects.update_or_create(

                i,
                **{
                    'devices_id': dev.id,
                    'u_id': index
                },
            )

            index += 1

    def update_controls_group(self, conn, dev):
        number_group = self.config.get_number_group_output_item(conn)
        index = 1
        for i in number_group['settings']:
            print(i)
            if i['number'] <= 0 or i['is_use'] != 1:
                index += 1
                continue
            i.update({
                'alias': i['number'],
                'u_id': index,
                'devices_id': dev.id,
            })
            control_group = ControlGroup.objects.update_or_create(

                i,
                **{
                    'devices_id': dev.id,
                    'u_id': index
                },
            )

            index += 1

    def update_mock_control(self, conn, dev):
        res = self.config.get_mock_output_item(conn)
        index = 1
        for i in res['settings']:
            print(i)
            if i['number'] <= 0 or i['is_use'] != 1:
                index += 1
                continue
            i.update({
                'name': i['number'],
                'u_id': index,
                'devices_id': dev.id
            })
            controls = MockControls.objects.update_or_create(

                i,
                **{
                    'devices_id': dev.id,
                    'u_id': index
                },
            )
            index += 1

    def update_mock_sensor(self, conn, dev):
        res = self.config.get_mock_input_item(conn)
        index = 1
        for i in res['settings']:
            print(i, 'mock-sensor')
            if i['number'] <= 0 or i['is_use'] != 1:
                index += 1
                continue

            i.update({
                'devices_id': dev.id,
                'u_id': index
            })
            sensor = MockSensor.objects.update_or_create(

                i,
                **{
                    'devices_id': dev.id,
                    'u_id': index
                },
            )
            index += 1

    def update_number_input(self, conn, dev):
        res = self.config.get_number_input_item(conn)
        index = 1
        for i in res['settings']:
            print(i)
            if i['is_use'] != 1 or i['number'] <= 0:
                index += 1
                continue
            i.update({
                'devices_id': dev.id,
                "u_id": index
            })
            sensor = NumberSensor.objects.update_or_create(

                i,
                **{
                    'devices_id': dev.id,
                    'number': i['number']
                },
            )
            index += 1


class Heartbeat(EventHandle):
    '''
        心跳事件响应，\x09
    '''

    def handle(self, conn: Connection, data: Message, serial_number: str = '') -> None:
        '''
        处理心跳包相应
        '''
        print('heart heat++++++++++++=')
        dev: Devices = Devices.objects.filter(
            device_serial_number=serial_number).first()
        if dev:
            dev.status = True

        callback: Message = HeartbeatSerializer.pack({
            'status': 11,
            'result': 0,
            'date': time.strftime('%Y%m%d')[2:],
            'time': time.strftime('%H%M%S')
        }, request_type.PAYLOAD_RSP_TO_MCU_HEART_BEAT)

        conn.send(callback, save=False)


class RegisterDevicesEventHandle(EventHandle):
    """
    注册事件响应  \x07
    """

    def handle(self, conn: Connection, data: Message, serial_number: str = ''):
        print('++++++ register')
        res: Message = RegisterMessageSerializer.pack({
            'status': 0
        }, request_type.PAYLOAD_RSP_TO_MCU_REGITSTER)
        if conn:
            conn.send(res, save=False)


class ReadConfigItemHandle(EventHandle):
    """
        读取设备中的配置项
    """

    def handle_redis(self, conn: socket, data: dict, serial_number: str = ''):
        """
            redis
        """
        res = self.handle(conn, data)
        manage = TCPManager.get_instance()
        manage.answer(data['_call_back_id'], {'data': res.hex()})

    def handle(self, conn: socket, data: dict, serial_number: str = ''):
        """
            正常相应，该事件设备不会向设备发送

        """
        msg = data['msg']
        req = readConfigPackagerItem.pack(
            {'item': msg['item']}, request_type.PAYLOAD_REQ_TO_MCU_GET_CLASS)
        conn.send(req)
        payload = req.wait()[0]
        return payload


class ReadConfigEventHandle(EventHandle):
    """
        读取设备中的配置项
    """

    def handle_redis(self, conn: socket, data: dict, serial_number: str = ''):
        """
            redis
        """
        res = self.handle(conn, data)
        manage = TCPManager.get_instance()
        manage.answer(data['_call_back_id'], {'data': res.hex()})

    def handle(self, conn: socket, data: dict, serial_number: str = '') -> bytes:
        """
            正常相应，该事件设备不会向设备发送

        """
        msg: dict = data['msg']
        length, offset, item = self.config.get(msg['item'], msg['subitem'])
        req: Message = ReadConfigSerializer.pack(
            {'item': item, 'length': length, 'offset': offset}, request_type.PAYLOAD_REQ_TO_MCU_GET_PARAM)
        conn.send(req)
        res = req.wait()[0]

        return res


class WriteConfigItemHandle(EventHandle):
    """
        更改设备中的配置项
    """

    def handle_redis(self, conn: socket, data: dict, serial_number: str = ''):
        """
            redis
        """
        res: bytes = self.handle(conn, data, serial_number)
        manage: TCPManager = TCPManager.get_instance()
        manage.answer(data['_call_back_id'], {'data': res})

    def handle(self, conn: socket, data: dict, serial_number: str = ''):
        """
            正常相应，该事件设备不会向设备发送

        """
        msg: dict = data['msg']
        dev = Devices.objects.filter(
            device_serial_number=serial_number
        ).first()
        length, offset, item = self.config.get(msg['item'], msg['subitem'])
        if isinstance(msg['data'], int):
            data: bytes = fill_data(msg['data'], length, left=True)
        elif isinstance(msg['data'], str):
            data: bytes = fill_data(msg['data'], length)
        else:
            # 获取下标
            index = msg.get('index')
            print(index)
            if index:
                offset += length * index
                # 当传递进来的是list时更改长度为 list里面item的总长度
            length = len(msg['data']) * length

            serial = self._get_list_serializers(
            )[f"{msg['item']}_{msg['subitem']}"]
            print(serial_number)

            data = serial.pack(msg['data'], many=True, pack=False, dev=dev)

        req: Message = WriteConfigSerializer.pack(
            {
                'item': item,
                'length': length,
                'offset': offset,
                'data': data
            },
            request_type.PAYLOAD_REQ_TO_MCU_SET_PARAM,
            dev=dev
        )
        index = 1

        start = 1
        print(req.hex())

        conn.send(req)
        data = req.wait()[0]
        return data[-1] == 0 if data else True

    def _get_list_serializers(self):
        return {
            'remote_network_remote_ips': RemoteNetworkSerializer.remote_ips,
            'line_485_settings': Setting_485.settings,
            'number_input_settings': NumberInputSetting.settings,
            'mock_input_settings': MockInputSetting.settings,
            'sensor_485_settings': SensorMessage,
            'number_group_output_settings': NumberGroupOutPutSerializer.settings,
            'mock_output_settings': MockOutPutSerializer.settings,
            'number_output_settings': NumberOutPutSerializer.settings,
            'auto_rule_settings': AutoRuleSerializer
        }


class SettingNetWorkEventHandle(EventHandle):
    """
        设置服务器
    """

    def handle(self, conn: socket, data, serial_number: str = '') -> bytes:
        msg = data['msg']
        req = SettingSerializer.pack({
            'ip': msg['ip'],
            'port': msg['port']
        }, request_type.PAYLOAD_REQ_FR_MCU_START_REPORT_HIS_DATA)
        print(req.hex())
        try:
            conn.send(req)
        except Exception as e:
            pass
        return True

    def handle_redis(self, conn: socket, data: dict, serial_number: str = ''):
        res = self.handle(conn, data)
        manage = TCPManager.get_instance()
        manage.answer(data['_call_back_id'], {'data': res})
        _, res = self._read(conn, timeout=5)


class SensorDataEventHandle(EventHandle):
    """
    处理自动上报数据
    """

    def handle(self, conn: socket, data: dict, serial_number: str = ''):
        dev = Devices.objects.filter(
            device_serial_number=serial_number).first()
        data = self._parse_content(data)
        parse_str = [
            ['<i', '<I', '<H', '<f', '<?'],
            ['>i', '>I', '>H', '>f', '>?'],
        ]

        for i in data:
            if i['input_type'] == 2:    
                """
                    485传感器
                """
                sensor: Sensor = Sensor.objects.filter(
                    devices__device_serial_number=serial_number, input_number=i['input_channel']).first()

                if dev and sensor:
                    format_str = parse_str[sensor.end_type][sensor.data_format]
                    i['data'] = unpack(format_str, i['data'])[0]
                    print(i)
                    sensor.append(i)
                    if i['data'] > sensor.warn_max_value:
                        for user in sensor.devices.user.all():
                            print(user.username)
                            self.manager.emit('warning', {
                                'data': i,
                                'msg': f'{sensor.devices.alias}下的{sensor.name}传感器数值超上限'
                            }, room=user.username)
                        for user in User.objects.filter(is_superuser=True).all():
                            self.manager.emit('warning', {
                                'data': i,
                                'msg': f'{sensor.devices.alias}下的{sensor.name}传感器数值超上限'
                            }, room=user.username)

                    elif i['data'] < sensor.warn_min_value:
                        for user in sensor.devices.user.all():
                            print(user.username)
                            self.manager.emit('warning', {
                                'data': i,
                                'msg': f'{sensor.devices.alias}下的{sensor.name}传感器数值超下限'
                            }, room=user.username)
                        for user in User.objects.filter(is_superuser=True).all():
                            self.manager.emit('warning', {
                                'data': i,
                                'msg': f'{sensor.devices.alias}下的{sensor.name}传感器数值超下限'
                            }, room=user.username)
                    i['data'] = str(round(i['data'],3))
                    History.objects.create(devices_id=dev.id, sensor_id=sensor.id, **i)

            elif i['input_type'] == 1:

                i['data'] = unpack('<f', i['data'])[0]
                i['data']  = round(i['data'],3)
                sensor: MockSensor = MockSensor.objects.filter(
                    devices__device_serial_number=serial_number, number=i['input_channel']).first()
                if not sensor:
                    continue

                if i['data'] > sensor.warn_max_value:
                    for user in sensor.devices.user.all():
                        print(user.username)
                        self.manager.emit('warning', {
                            'data': i,
                            'msg': f'{sensor.devices.alias}下的{sensor.name}传感器数值超上限'
                        }, room=user.username)
                    for user in User.objects.filter(is_superuser=True).all():
                        self.manager.emit('warning', {
                            'data': i,
                            'msg': f'{sensor.devices.alias}下的{sensor.name}传感器数值超上限'
                        }, room=user.username)

                elif i['data'] < sensor.warn_min_value:
                    for user in sensor.devices.user.all():
                        print(user.username)
                        self.manager.emit('warning', {
                            'data': i,
                            'msg': f'{sensor.devices.alias}下的{sensor.name}传感器数值超下限'
                        }, room=user.username)
                    for user in User.objects.filter(is_superuser=True).all():
                        self.manager.emit('warning', {
                            'data': i,
                            'msg': f'{sensor.devices.alias}下的{sensor.name}传感器数值超下限'
                        }, room=user.username)
                i['data'] = str(round(i['data'],3))
                History.objects.create(
                    devices_id=dev.id, mock_sensor_id=sensor.id, **i)
        if conn:
            res: bytes = SensorDataResPackager.pack(
                {'code': 20, 'result': 0}, request_type.PAYLOAD_RSP_TO_MCU_UP_REPORT)
            
            conn.send(res)

    def _parse_content(self, data: Message):

        return SensorDataReqPackager.unpack_subserializer(data.content)


class LoadALLConfig(EventHandle):
    """
    从设备加载配置项， item 可以以 . 分割传递多个配置项  默认加载所有
    """

    def handle_redis(self, conn, data, serial_number=''):
        res = {
            'sys': self.config.get_sys_item,
            'hard': self.config.get_hard_item,
            'remote_network': self.config.get_remote_network_item,
            'storage': self.config.get_cache_item_item,
            'line_485': self.config.get_line_485__item,
            'local_network': self.config.get_local_network_item,
            'number_input': self.config.get_number_input_item,
            'mock_input': self.config.get_mock_input_item,
            'sensor_485': self.config.get_total_line_item,
            'number_group_output': self.config.get_number_group_output_item,
            'number_output': self.config.get_number_output_item,
            'mock_output': self.config.get_mock_output_item,
        }
        items: str = data['msg'].get('items', 'all')

        if items != 'all':
            items = items.split('.')
            res = {k: v for k, v in res.items() if k in items}

        res = {
            k: method(conn) for k, method in res.items()
        }

        manage = TCPManager.get_instance()
        manage.answer(data['_call_back_id'], res)


class ReBootEventHandle(EventHandle):
    """
    重启设备
    """

    def handle_redis(self, conn: socket, data: dict, serial_number: str = ''):

        req = Message(b'\x2d\x00')
        conn.send(req)
        manage = TCPManager.get_instance()
        manage.answer(data['_call_back_id'], {'msg': '重启成功'})


class AutoUploadHandle(EventHandle):
    """
    自动上传配置 ， 基本废弃
    """

    def handle_redis(self, conn: socket, data: dict, serial_number: str = ''):
        msg = data['msg']

        req = AutoUploadSerializer.pack(
            {'status': msg['status']}, request_type.PAYLOAD_REQ_TO_MCU_ENABLE_UP_REPORT)
        conn.send(req)
        manage = TCPManager.get_instance()
        manage.answer(data['_call_back_id'], {'msg': '成功'})


class ReadControlStatusHandle(EventHandle):
    """
    获取继电器专状态
    """

    def handle_redis(self, conn: socket, data: dict, serial_number: str = ''):
        """
            读取当前设备继电器状态
        """
        msg = data['msg']
        req = ReadControlStatusSerializer.pack(
            msg, request_type.PAYLOAD_REQ_TO_MCU_GET_INPUT
        )
        conn.send(req)
        
        res = req.wait()
        print(res[0].hex())
        TCPManager.get_instance().answer(
            data['_call_back_id'],
            ControlStatusSerializer.unpack(res[0]),
        )


class UpdateControlStatusHandle(EventHandle):
    """
    写入继电器状态，打开  关闭  正转反转继电器
    """

    def handle_redis(self, conn: socket, data: dict, serial_number: str = ''):
        """
            修改当前继电器状态
        """
        msg = data['msg']
        req = WriteControlStatusSerializer.pack(
            msg, request_type.PAYLOAD_REQ_TO_MCU_SET_OUTPUT)

        conn.send(req)

        res = req.wait()[0]

        TCPManager.get_instance().answer(
            data['_call_back_id'],
            {
                'data': b'\x00' == res[1] if res else False
            },
        )


class CloseConnHandle(EventHandle):
    """

    关闭当前socket连接
    """

    def handle_redis(self, conn: socket, data: dict, serial_number: str = ''):
        conn.close()
        TCPManager.get_instance().answer(data['_call_back_id'], True)


class TransmissionEventHandle(EventHandle):
    """
      485数据透传
    """

    def handle_redis(self, conn: socket, data: dict, serial_number: str = ''):
        msg = data['msg']
        msg['data'] = bytes.fromhex(msg['data'].replace(' ', ''))
        msg['data_length'] = len(msg['data'])
        req = TransmissionSerializer.pack(
            msg, request_type.PAYLOAD_REQ_TO_MCU_DOWN_TRANS)
        conn.send(req)
        typ, res = self._read(conn)

        TCPManager.get_instance().answer(
            data['_call_back_id'],
            {
                'hex': res.hex()
            }
        )


class AutoRuleActionsEventHandle(EventHandle):
    """
    更改自动规则  该方法已经废弃
    """

    def handle_redis(self, conn, msg, serial_number):
        data = msg['msg']

        getattr(self, data['type'])(conn, data, serial_number)

        TCPManager.get_instance().answer(msg['_call_back_id'], {
            'data': True
        })

    def update(self, conn, msg, serial_number):
        data = AutoRuleSerializer.pack(msg['data'], pack=False)
        req = AutoRuleAction.pack({
            'data': data,
            'action': 1
        }, request_type.PAYLOAD_REQ_TO_MCU_SET_AUTO_RULE)
        conn.send(req)

        _, _ = self._read(conn, timeout=2)

        return True

    def create(self, conn, msg, serial_number):
        data = AutoRuleSerializer.pack(msg['data'], pack=False)

        req = AutoRuleAction.pack({
            'data': data,
            'action': 1
        }, request_type.PAYLOAD_REQ_TO_MCU_SET_AUTO_RULE)

        conn.send(req)

        _, _ = self._read(conn, timeout=2)

        return True

    def delete(self, conn, msg, serial_number):
        data = AutoRuleSerializer.pack(msg['data'], pack=False)

        req = AutoRuleAction.pack({
            'data': data,
            'action': 2
        }, request_type.PAYLOAD_REQ_TO_MCU_SET_AUTO_RULE)

        conn.send(req)

        _, _ = self._read(conn, timeout=2)

        return True
